﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Data;
using System.Runtime.Remoting.Messaging;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Collections;
using Pixysoft.Framework.Reflection;
using Pixysoft.Framework.Noebe.Orm.Controller;
using Pixysoft.Framework.Verifications;

namespace Pixysoft.Framework.Noebe.Orm.Core
{
    class DynamicOrmProxy : DynamicProxy
    {
        private Type ormType = null;

        private ObjectProxy _proxy = null;

        /// <summary>
        /// 针对查询
        /// </summary>
        /// <param name="type"></param>
        /// <param name="proxy"></param>
        public DynamicOrmProxy(Type ormInterfaceType, ObjectProxy proxy)
            : base(ormInterfaceType)
        {
            if (!ormInterfaceType.IsInterface)
                throw Exceptions.UnexpectedResultException("only support interface type.");

            this.ormType = ormInterfaceType;

            this._proxy = proxy;
        }


        public T GetTransparentProxy<T>()
        {
            return (T)base.GetTransparentProxy();
        }

        public override IDynamicMethodReturnMessage Invoke(IDynamicMethodCallMessage inMessageCall)
        {
            MethodInfo method = inMessageCall.MethodInfo;

            if (method == null)
                return base.CreateExceptionReturnMessage(new Exception("only support property."), inMessageCall);

            if (method == typeof(object).GetMethod("GetType"))
            {
                return base.CreateReturnMessage(GetProxyType(), inMessageCall);
            }
            else if (method == typeof(object).GetMethod("Equals", new Type[] { typeof(object) }))
            {
                return base.CreateReturnMessage(false, inMessageCall);
            }
            else if (method == typeof(object).GetMethod("GetHashCode"))
            {
                return base.CreateReturnMessage(this.GetHashCode(), inMessageCall);
            }
            else if (method == typeof(object).GetMethod("ToString"))
            {
                return base.CreateReturnMessage(this.ToString(), inMessageCall);
            }


            switch (ReflectHelper.GetMethodType(method))
            {
                case MethodType.Getter:
                    {
                        return PreprocessGet(inMessageCall);
                    }
                case MethodType.Setter:
                    {
                        return PreprocessSet(inMessageCall);
                    }
                case MethodType.AddToList:
                    {
                        //要求传入参数必须类型一致，没有null

                        return PreprocessAddToList(inMessageCall);
                    }
                case MethodType.RemoveFromList:
                    {
                        return PreprocessRemoveFromList(inMessageCall);
                    }
                case MethodType.AddToValueList:
                    {
                        return PreprocessAddToValueList(inMessageCall);
                    }
                case MethodType.RemoveFromValueList:
                    {
                        return PreprocessRemoveFromValueList(inMessageCall);
                    }
                case MethodType.AddToBinaryList:
                    {
                        return PreprocessAddToBinaryList(inMessageCall);
                    }
                case MethodType.RemoveFromBinaryList:
                    {
                        return PreprocessRemoveFromBinaryList(inMessageCall);
                    }
                case MethodType.Contains:
                    {
                        return PreprocessContains(inMessageCall);
                    }
                case MethodType.Select:
                    {
                        return PreprocessSelect(inMessageCall);
                    }
                case MethodType.Manual:
                case MethodType.Unknown:
                default:
                    return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);
            }
        }

        private IDynamicMethodReturnMessage PreprocessGet(IDynamicMethodCallMessage inMessageCall)
        {
            string fieldName = Regex.Replace(inMessageCall.MethodInfo.Name, "GET_", "", RegexOptions.IgnoreCase).Trim().ToUpper();

            switch (ReflectHelper.GetOrmTypeByType(inMessageCall.MethodInfo.ReturnType))
            {
                case DotNetPropertyType.Enum:
                    {
                        return GetEnum(inMessageCall, fieldName);
                    }
                case DotNetPropertyType.Value:
                    {
                        return GetValue(inMessageCall, fieldName);
                    }
                case DotNetPropertyType.BinaryList:
                case DotNetPropertyType.Binary:
                    {
                        return GetBinary(inMessageCall, fieldName);
                    }
                case DotNetPropertyType.Interface:
                case DotNetPropertyType.Object:
                    {
                        return GetObject(inMessageCall, fieldName);
                    }


                case DotNetPropertyType.ValueList:
                case DotNetPropertyType.EnumList:
                    {
                        return GetValueList(inMessageCall, fieldName);
                    }
                case DotNetPropertyType.ValueArray:
                case DotNetPropertyType.EnumArray:
                    {
                        return GetValueCollection(inMessageCall, fieldName);
                    }


                case DotNetPropertyType.ObjectArray:
                case DotNetPropertyType.InterfaceArray:
                    {
                        return GetCollection(inMessageCall, fieldName);
                    }
                case DotNetPropertyType.InterfaceList:
                case DotNetPropertyType.ObjectList:
                    {
                        return GetList(inMessageCall, fieldName);
                    }

                default:
                    return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);
            }
        }

        private IDynamicMethodReturnMessage PreprocessSet(IDynamicMethodCallMessage inMessageCall)
        {
            if (inMessageCall.InArgCount != 1)
                return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);

            string fieldName = Regex.Replace(inMessageCall.MethodInfo.Name, "SET_", "", RegexOptions.IgnoreCase).Trim().ToUpper();

            object value = inMessageCall.GetInArg(0);

            switch (ReflectHelper.GetOrmTypeByType(inMessageCall.MethodInfo.GetParameters()[0].ParameterType))
            {
                case DotNetPropertyType.Enum:
                    {
                        SetEnum(fieldName, value);

                        break;
                    }
                case DotNetPropertyType.Value:
                    {
                        SetValue(fieldName, value);

                        break;
                    }
                case DotNetPropertyType.BinaryList:
                case DotNetPropertyType.Binary:
                    {
                        SetBinary(fieldName, value);

                        break;
                    }
                case DotNetPropertyType.Interface:
                case DotNetPropertyType.Object:
                    {
                        SetObject(fieldName, value);

                        break;
                    }


                case DotNetPropertyType.ValueList:
                case DotNetPropertyType.EnumList:
                    {
                        SetValueList(fieldName, value);

                        break;
                    }
                case DotNetPropertyType.ValueArray:
                case DotNetPropertyType.EnumArray:
                    {
                        SetValueCollection(fieldName, value);

                        break;
                    }

                case DotNetPropertyType.ObjectArray:
                case DotNetPropertyType.InterfaceArray:
                    {
                        SetCollection(fieldName, value);

                        break;
                    }
                case DotNetPropertyType.InterfaceList:
                case DotNetPropertyType.ObjectList:
                    {
                        SetList(fieldName, value);

                        break;
                    }

                default:
                    return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);
            }

            return base.CreateVoidReturnMessage(inMessageCall);
        }



        private IDynamicMethodReturnMessage PreprocessAddToList(IDynamicMethodCallMessage inMessageCall)
        {
            string fieldName = Regex.Replace(inMessageCall.MethodInfo.Name, "ADDTO(_){0,1}", "", RegexOptions.IgnoreCase).Trim().ToUpper();

            if (string.IsNullOrEmpty(fieldName))
                throw Exceptions.UnexpectedResultException("unknown fieldname in select:{0}", this.ormType.Name);

            if (inMessageCall.InArgCount != 1)//Only support 1 param method such as Property
                return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);

            ObjectProxy _proxy = GetProxy();

            object value = inMessageCall.GetInArg(0);


            List<ObjectProxy> proxyList = new List<ObjectProxy>();

            if (value == null)
                return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);

            proxyList = ConversionController.ObjectListToProxyList(value as IList);

            if (proxyList.Count == 0)
                return base.CreateVoidReturnMessage(inMessageCall);

            _proxy.AddToList(fieldName, proxyList.ToArray());

            return base.CreateVoidReturnMessage(inMessageCall);
        }

        private IDynamicMethodReturnMessage PreprocessRemoveFromList(IDynamicMethodCallMessage inMessageCall)
        {
            if (inMessageCall.InArgCount != 1)//Only support 1 param method such as Property
                return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);

            string fieldName = Regex.Replace(inMessageCall.MethodInfo.Name, "REMOVEFROM(_){0,1}", "", RegexOptions.IgnoreCase).Trim().ToUpper();

            if (string.IsNullOrEmpty(fieldName))
                throw Exceptions.UnexpectedResultException("unknown fieldname in select:{0}", this.ormType.Name);

            if (inMessageCall.InArgCount != 1)//Only support 1 param method such as Property
                return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);


            ObjectProxy _proxy = GetProxy();

            List<ObjectProxy> proxyList = new List<ObjectProxy>();


            object value = inMessageCall.GetInArg(0);

            if (value == null)
                return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);


            proxyList = ConversionController.ObjectListToProxyList(value as IList);

            if (proxyList.Count == 0)
                return base.CreateVoidReturnMessage(inMessageCall);


            _proxy.RemoveFromList(fieldName, proxyList.ToArray());

            return base.CreateVoidReturnMessage(inMessageCall);
        }

        private IDynamicMethodReturnMessage PreprocessAddToValueList(IDynamicMethodCallMessage inMessageCall)
        {
            string fieldName = Regex.Replace(inMessageCall.MethodInfo.Name, "ADDTO(_){0,1}", "", RegexOptions.IgnoreCase).Trim().ToUpper();

            if (string.IsNullOrEmpty(fieldName))
                throw Exceptions.UnexpectedResultException("unknown fieldname in select:{0}", this.ormType.Name);

            if (inMessageCall.InArgCount != 1)//Only support 1 param method such as Property
                return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);

            ObjectProxy _proxy = GetProxy();

            IList valuelist = inMessageCall.GetInArg(0) as IList;

            if (valuelist == null || valuelist.Count == 0)
                return base.CreateVoidReturnMessage(inMessageCall);

            _proxy.AddToValueList(fieldName, valuelist);

            return base.CreateVoidReturnMessage(inMessageCall);
        }

        private IDynamicMethodReturnMessage PreprocessRemoveFromValueList(IDynamicMethodCallMessage inMessageCall)
        {
            string fieldName = Regex.Replace(inMessageCall.MethodInfo.Name, "REMOVEFROM(_){0,1}", "", RegexOptions.IgnoreCase).Trim().ToUpper();

            if (string.IsNullOrEmpty(fieldName))
                throw Exceptions.UnexpectedResultException("unknown fieldname in select:{0}", this.ormType.Name);

            if (inMessageCall.InArgCount != 1)//Only support 1 param method such as Property
                return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);

            ObjectProxy _proxy = GetProxy();

            IList valuelist = inMessageCall.GetInArg(0) as IList;

            if (valuelist == null || valuelist.Count == 0)
                return base.CreateVoidReturnMessage(inMessageCall);


            _proxy.RemoveFromValueList(fieldName, valuelist);

            return base.CreateVoidReturnMessage(inMessageCall);
        }

        private IDynamicMethodReturnMessage PreprocessAddToBinaryList(IDynamicMethodCallMessage inMessageCall)
        {
            string fieldName = Regex.Replace(inMessageCall.MethodInfo.Name, "ADDTO(_){0,1}", "", RegexOptions.IgnoreCase).Trim();

            if (string.IsNullOrEmpty(fieldName))
                throw Exceptions.UnexpectedResultException("unknown fieldname in select:{0}", this.ormType.Name);

            if (inMessageCall.InArgCount != 1)
                return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);

            PropertyInfo info = GetProxyType().GetProperty(fieldName);

            if (info == null)
                return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);

            Type propertyType = info.PropertyType;

            ObjectProxy _proxy = GetProxy();

            byte[] midValue_get = _proxy.GetValue(fieldName) as byte[];

            IList binarylist = inMessageCall.GetInArg(0) as IList;

            if (binarylist == null || binarylist.Count == 0)
                return base.CreateVoidReturnMessage(inMessageCall);

            try
            {
                IList userValue = null;

                if (midValue_get == null)
                    userValue = Activator.CreateInstance(propertyType) as IList;
                else
                    userValue = Pixysoft.IO.XmlImporter.Instance.XmlDeserialize(midValue_get, propertyType) as IList;

                foreach (object obj in binarylist)
                    userValue.Add(obj);

                byte[] midvalue_set = StringCoder.StringToByte(Pixysoft.IO.XmlExporter.Instance.XmlSerialize(userValue));

                _proxy.SetValue(fieldName, midvalue_set);

                return base.CreateVoidReturnMessage(inMessageCall);
            }
            catch (Exception ex)
            {
                return base.CreateExceptionReturnMessage(ex, inMessageCall);
            }
        }

        private IDynamicMethodReturnMessage PreprocessRemoveFromBinaryList(IDynamicMethodCallMessage inMessageCall)
        {
            return base.CreateExceptionReturnMessage(new Exception("unsupported object! only value!"), inMessageCall);
        }



        private IDynamicMethodReturnMessage PreprocessContains(IDynamicMethodCallMessage inMessageCall)
        {
            if (inMessageCall.InArgCount != 1)
                return base.CreateExceptionReturnMessage(new Exception("unknown method type."), inMessageCall);

            string fieldName = Regex.Replace(inMessageCall.MethodInfo.Name, "(_){0,1}CONTAINS", "", RegexOptions.IgnoreCase).Trim().ToUpper();

            if (string.IsNullOrEmpty(fieldName))
                throw Exceptions.UnexpectedResultException("unknown fieldname in select:{0}", this.ormType.Name);

            ObjectProxy _proxy = GetProxy();

            object value = inMessageCall.GetInArg(0);

            if (value == null)
                return base.CreateReturnMessage(false, inMessageCall);



            string tObjName = ReflectHelper.GetFieldInterfaceTypeName(GetProxyType(), fieldName);

            string upkName = ReflectHelper.GetUpkName(GetProxyType());

            bool result = _proxy.Contains(fieldName, tObjName, upkName, value);

            return base.CreateReturnMessage(result, inMessageCall);
        }

        private IDynamicMethodReturnMessage PreprocessSelect(IDynamicMethodCallMessage inMessageCall)
        {
            string fieldName = Regex.Replace(inMessageCall.MethodInfo.Name, "SELECT(_){0,1}", "", RegexOptions.IgnoreCase).Trim().ToUpper();

            if (string.IsNullOrEmpty(fieldName))
                throw Exceptions.UnexpectedResultException("unknown fieldname in select:{0}", this.ormType.Name);

            Type ormInterfaceType = inMessageCall.MethodInfo.ReturnType;


            object value = inMessageCall.GetInArg(0);

            if (value == null)
                return base.CreateReturnMessage(Pixysoft.Tools.ParserHelper.GetNullValue(ormInterfaceType), inMessageCall);


            string tObjName = ReflectHelper.GetInterfaceTypeName(ormInterfaceType);

            //09-12-09 bug, upk from return type, not current type.

            string upkName = ReflectHelper.GetUpkName(ormInterfaceType);

            ObjectProxy _proxy = GetProxy();

            ObjectProxy midValue = _proxy.Select(fieldName, tObjName, upkName, value);

            //09-12-09 bug 

            if (midValue == null)
            {
                return base.CreateReturnMessage(Pixysoft.Tools.ParserHelper.GetNullValue(ormInterfaceType), inMessageCall);
            }

            object userValue = ConversionController.ProxyToObject(ormInterfaceType, midValue);

            return base.CreateReturnMessage(userValue, inMessageCall);
        }



        private void SetEnum(string fieldName, object value)
        {
            GetProxy().SetValue(fieldName, value);
        }

        private void SetValue(string fieldName, object value)
        {
            GetProxy().SetValue(fieldName, value);
        }

        private void SetObject(string fieldName, object value)
        {
            ObjectProxy _proxy = GetProxy();

            if (value == null)
                _proxy.DeleteObject(fieldName);
            else
            {
                ObjectProxy midproxy = ConversionController.ObjectToProxy(value);

                if (midproxy == null)
                    return;

                _proxy.SetObject(fieldName, midproxy);
            }
        }

        private void SetList(string fieldName, object value)
        {
            ObjectProxy _proxy = GetProxy();

            if (value == null)
                _proxy.DeleteList(fieldName);
            else
            {
                IList list = value as IList;

                if (list == null)
                    return;

                List<ObjectProxy> proxyList = ConversionController.ObjectListToProxyList(list);

                _proxy.SetList(fieldName, proxyList);
            }
        }

        private void SetCollection(string fieldName, object value)
        {
            SetList(fieldName, value);
        }

        private void SetValueList(string fieldName, object value)
        {
            ObjectProxy _proxy = GetProxy();

            if (value == null)
            {
                _proxy.DeleteValueList(fieldName);
            }
            else
            {
                IList list = value as IList;

                if (list == null)
                    return;

                _proxy.SetValueList(fieldName, list);
            }
        }

        private void SetValueCollection(string fieldName, object value)
        {
            SetValueList(fieldName, value);
        }

        private void SetBinary(string fieldName, object value)
        {
            if (value == null)
            {
                GetProxy().DeleteValue(fieldName);
            }
            else
            {
                try
                {
                    byte[] midvalue = Pixysoft.IO.XmlExporter.Instance.XmlByteSerialize(value);

                    GetProxy().SetValue(fieldName, midvalue);
                }
                catch (Exception ex)
                {
                    LoggerHelper.Error("object cannot by serialized. fieldname = {0}", fieldName);

                    LoggerHelper.Error(ex.ToString());

                    throw new Exception("object cannot be serialized." + ex.ToString());
                }
            }
        }




        private IDynamicMethodReturnMessage GetObject(IDynamicMethodCallMessage call, string fieldName)
        {
            ObjectProxy _proxy = GetProxy();

            Type ormInterfaceType = call.MethodInfo.ReturnType;

            string tObjName = ReflectHelper.GetInterfaceTypeName(ormInterfaceType);

            ObjectProxy midValue = _proxy.GetObject(fieldName, tObjName);

            if (midValue == null)
                return base.CreateReturnMessage(null, call);

            object userValue = ConversionController.ProxyToObject(ormInterfaceType, midValue);

            return base.CreateReturnMessage(userValue, call);
        }

        private IDynamicMethodReturnMessage GetValue(IDynamicMethodCallMessage call, string fieldName)
        {
            ObjectProxy _proxy = GetProxy();

            Type ormInterfaceType = call.MethodInfo.ReturnType;

            object midValue = _proxy.GetValue(fieldName);

            object userValue = ConversionController.ObjectToValue(ormInterfaceType, midValue);

            return base.CreateReturnMessage(userValue, call);
        }

        private IDynamicMethodReturnMessage GetEnum(IDynamicMethodCallMessage call, string fieldName)
        {
            ObjectProxy _proxy = GetProxy();

            object midValue = _proxy.GetValue(fieldName);

            if (midValue == null || midValue == DBNull.Value)
            {
                object userValue = ConversionController.StringToEnum(call.MethodInfo.ReturnType);

                return base.CreateReturnMessage(userValue, call);
            }
            else
            {
                object userValue = ConversionController.StringToEnum(call.MethodInfo.ReturnType, midValue.ToString());

                return base.CreateReturnMessage(userValue, call);
            }
        }

        private IDynamicMethodReturnMessage GetBinary(IDynamicMethodCallMessage call, string fieldName)
        {
            ObjectProxy _proxy = GetProxy();

            Type ormInterfaceType = call.MethodInfo.ReturnType;

            byte[] midValue = _proxy.GetValue(fieldName) as byte[];

            //[080630]如果是List，则返回空list

            if (midValue == null)
            {
                if (ormInterfaceType.IsGenericType)
                    return base.CreateReturnMessage(Activator.CreateInstance(ormInterfaceType), call);
                else
                    return base.CreateReturnMessage(null, call);
            }

            try
            {
                object userValue = Pixysoft.IO.XmlImporter.Instance.XmlDeserialize(midValue, ormInterfaceType);

                return base.CreateReturnMessage(userValue, call);
            }
            catch (Exception ex)
            {
                LoggerHelper.Error("object cannot by serialized. fieldname = {0}", fieldName);

                LoggerHelper.Error(ex.ToString());

                return base.CreateExceptionReturnMessage(new Exception("object cannot be deserialized."), call);
            }
        }

        private IDynamicMethodReturnMessage GetCollection(IDynamicMethodCallMessage call, string fieldName)
        {
            ObjectProxy _proxy = GetProxy();

            Type type = call.MethodInfo.ReturnType;


            Type elementType = type.GetElementType();


            string tObjName = ReflectHelper.GetInterfaceTypeName(elementType);

            List<ObjectProxy> midValue = _proxy.GetList(fieldName, tObjName);

            IList userValues = ConversionController.ProxyListToCollection(type, midValue);

            return base.CreateReturnMessage(userValues, call);
        }

        private IDynamicMethodReturnMessage GetList(IDynamicMethodCallMessage call, string fieldName)
        {
            ObjectProxy _proxy = GetProxy();

            Type type = call.MethodInfo.ReturnType;

            Type[] argTypes = type.GetGenericArguments();

            if (argTypes == null || argTypes.Length != 1)
                return base.CreateExceptionReturnMessage(new Exception("only support one generic list."), call);

            string tObjName = ReflectHelper.GetInterfaceTypeName(argTypes[0]);

            List<ObjectProxy> midValue = _proxy.GetList(fieldName, tObjName);

            object userValue = ConversionController.ProxyListToObjectList(type, midValue);

            return base.CreateReturnMessage(userValue, call);
        }

        private IDynamicMethodReturnMessage GetValueCollection(IDynamicMethodCallMessage call, string fieldName)
        {
            ObjectProxy _proxy = GetProxy();

            Type type = call.MethodInfo.ReturnType;

            IList list = _proxy.GetValueList(fieldName);

            IList returnvalues = ConversionController.IListToValueCollection(type, list);

            return base.CreateReturnMessage(returnvalues, call);
        }

        private IDynamicMethodReturnMessage GetValueList(IDynamicMethodCallMessage call, string fieldName)
        {
            ObjectProxy _proxy = GetProxy();

            Type type = call.MethodInfo.ReturnType;

            IList list = _proxy.GetValueList(fieldName);

            IList returnlist = ConversionController.IListToValueList(type, list);

            return base.CreateReturnMessage(returnlist, call);
        }



        internal ObjectProxy GetProxy()
        {
            if (_proxy == null)
            {
                throw Exceptions.UnexpectedResultException("missing object proxy in orm proxy.");
            }

            return _proxy;
        }

        internal DataRow GetObjectRow()
        {
            return GetProxy().GetObjectRow();
        }

        public Type GetProxyType()
        {
            return this.ormType;
        }

        public string GroupCode()
        {
            object value = GetProxy().GetValue(StringHelper.ORM_GROUPCODE);

            if (value == null)
                return null;
            else
                return value.ToString();
        }
    }

    class DynamicOrmProxy<T> : DynamicOrmProxy
    {
        public DynamicOrmProxy(ObjectProxy proxy)
            : base(typeof(T), proxy)
        {
        }

        public new T GetTransparentProxy()
        {
            return base.GetTransparentProxy<T>();
        }
    }
}
